/*
	Copyright 2010 Kwok Ho Yin

   	Licensed under the Apache License, Version 2.0 (the "License");
   	you may not use this file except in compliance with the License.
   	You may obtain a copy of the License at

    	http://www.apache.org/licenses/LICENSE-2.0

   	Unless required by applicable law or agreed to in writing, software
   	distributed under the License is distributed on an "AS IS" BASIS,
   	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   	See the License for the specific language governing permissions and
   	limitations under the License.
*/

package com.hykwok.CameraEffect;

import java.util.ArrayList;
import java.util.List;

import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PictureCallback;
import android.util.Log;
import android.view.SurfaceHolder;

public class MyCameraControl {
	private static final String TAG = "MyCameraControl";
	
	private static Camera		m_camera = null;
	private static int			m_camera_counter = 0;
	
	private boolean 			m_close_is_called = true;
	private boolean				m_preview_started = false;
	private boolean				m_previewholder_set = false;
	
	public Camera.Parameters	m_camera_param = null;
	
	public List<String>			m_support_picture_format = null;
	public List<String>			m_support_picture_size = null;
	public List<String>			m_support_preview_format = null;
	public List<String>			m_support_preview_size = null;
	public List<String>			m_support_preview_framerate = null;
	
	public int m_Selected_Antibanding = -1;
	public int m_Selected_ColorEffects = -1;
	public int m_Selected_FlashModes = -1;
	public int m_Selected_FocusModes = -1;
	public int m_Selected_PictureFormat = -1;
	public int m_Selected_PictureSize = -1;
	public int m_Selected_PreviewFormat = -1;
	public int m_Selected_PreviewSize = -1;
	public int m_Selected_PreviewFrameRate = -1;
	public int m_Selected_SceneMode = -1;
	public int m_Selected_WhiteBalance = -1;
	
	private long m_current_preview_frame_no = 0;
	private long m_current_taken_raw_frame_no = 0;
	private long m_current_taken_jpeg_frame_no = 0;
	
	public MyCameraControl() {
		try {
			Log.d(TAG, "Constructor: (Before) counter = " + m_camera_counter);
			
			// create camera object if necessary
			if(m_camera_counter == 0) {
				m_camera = Camera.open();
				
				m_camera.setErrorCallback(m_error_cb);
				
				m_camera_param = m_camera.getParameters();
				
				// Setup call back functions
				if(m_camera_param.getFocusMode().contentEquals(Camera.Parameters.FOCUS_MODE_FIXED) == false) {
					m_camera.autoFocus(m_auto_focus_cb);
				}
			} else {
				m_camera_param = m_camera.getParameters();
			}
			
			m_camera_counter++;
			m_close_is_called = false;
			m_preview_started = false;
			
			m_current_preview_frame_no = 0;
			m_current_taken_raw_frame_no = 0;
			m_current_taken_jpeg_frame_no = 0;
			
			Log.d(TAG, "Constructor: (After) counter = " + m_camera_counter);
			
			// setup string lists
			m_support_picture_format = new ArrayList<String>();
			m_support_picture_size = new ArrayList<String>();
			m_support_preview_format = new ArrayList<String>();
			m_support_preview_size = new ArrayList<String>();
			m_support_preview_framerate = new ArrayList<String>();
			
			m_support_picture_format.clear();
			
			for (Integer x : m_camera_param.getSupportedPictureFormats()) {
				switch(x.intValue()) {
				case PixelFormat.JPEG:
					m_support_picture_format.add("JPEG");
					break;
					
				case PixelFormat.YCbCr_420_SP:
					m_support_picture_format.add("NV21");
					break;
					
				case PixelFormat.YCbCr_422_I:
					m_support_picture_format.add("YUY2");
					break;
					
				case PixelFormat.YCbCr_422_SP:
					m_support_picture_format.add("NV16");
					break;
					
				case PixelFormat.RGB_565:
					m_support_picture_format.add("RGB565");
					break;
					
				default:
					m_support_picture_format.add("Unknown:" + x);
					break;
				}
			}
			
			m_support_preview_format.clear();
			
			for (Integer x : m_camera_param.getSupportedPreviewFormats()) {
				switch(x.intValue()) {
				case PixelFormat.JPEG:
					m_support_preview_format.add("JPEG");
					break;
					
				case PixelFormat.YCbCr_420_SP:
					m_support_preview_format.add("NV21");
					break;
					
				case PixelFormat.YCbCr_422_I:
					m_support_preview_format.add("YUY2");
					break;
					
				case PixelFormat.YCbCr_422_SP:
					m_support_preview_format.add("NV16");
					break;
					
				case PixelFormat.RGB_565:
					m_support_preview_format.add("RGB565");
					break;
					
				default:
					m_support_preview_format.add("Unknown:" + x);
					break;
				}
			}
			
			m_support_preview_framerate.clear();
			
			for (Integer x : m_camera_param.getSupportedPreviewFrameRates()) {
				m_support_preview_framerate.add(Integer.toString(x.intValue()));
			}
			
			m_support_picture_size.clear();
			
			for(Camera.Size x : m_camera_param.getSupportedPictureSizes()) {
				m_support_picture_size.add(x.width + "x" + x.height);
			}
			
			m_support_preview_size.clear();
			
			for(Camera.Size x : m_camera_param.getSupportedPreviewSizes()) {
				m_support_preview_size.add(x.width + "x" + x.height);
			}
			
			InitSelectedParameters();
		} catch (Exception e) {
			Log.e(TAG, "CameraControl:" + e.toString());
		}
	}
	
	protected void finalize() {
		Close();
	}
	
	public void Close() {		
		if(m_close_is_called == false) {
			if(m_preview_started == true) {
				m_camera.stopPreview();
				m_preview_started = false;
			}
			
			m_camera_counter--;
			
			if(m_camera_counter < 0) {
				Log.e(TAG, "Close: (Final) counter = " + m_camera_counter);
			} else {
				Log.d(TAG, "Close: (Final) counter = " + m_camera_counter);
			}
			
			if(m_camera_counter == 0) {
				m_camera.release();
				m_camera = null;
			}
			
			m_close_is_called = true;
		}
	}
	
	private void InitSelectedParameters() {
		String			current_string;
		int				current_int;
		Camera.Size		current_size;
		int				pos;
		
		// preview format
		current_int = m_camera_param.getPreviewFormat();
		pos = 0;
		for (Integer x : m_camera_param.getSupportedPreviewFormats()) {
			if(x.intValue() == current_int) {
				m_Selected_PreviewFormat = pos;
				break;
			}
			pos++;
		}
		
		// preview size
		current_size = m_camera_param.getPreviewSize();
		pos = 0;
		for(Camera.Size x : m_camera_param.getSupportedPreviewSizes()) {
			if((x.width == current_size.width) && (x.height == current_size.height)) {
				m_Selected_PreviewSize = pos;
				break;
			}
			pos++;
		}
		
		// preview frame rate
		current_int = m_camera_param.getPreviewFrameRate();
		pos = 0;
		for (Integer x : m_camera_param.getSupportedPreviewFrameRates()) {
			if(x.intValue() == current_int) {
				m_Selected_PreviewFrameRate = pos;
				break;
			}
			pos++;
		}
		
		// picture format
		current_int = m_camera_param.getPictureFormat();
		pos = 0;
		for (Integer x : m_camera_param.getSupportedPictureFormats()) {
			if(x.intValue() == current_int) {
				m_Selected_PictureFormat = pos;
				break;
			}
			pos++;
		}
		
		// picture size
		current_size = m_camera_param.getPictureSize();
		pos = 0;
		for(Camera.Size x : m_camera_param.getSupportedPictureSizes()) {
			if((x.width == current_size.width) && (x.height == current_size.height)) {
				m_Selected_PictureSize = pos;
				break;
			}
			pos++;
		}
		
		// anti-banding
		current_string = m_camera_param.getAntibanding();
		pos = 0;		
		for ( String x : m_camera_param.getSupportedAntibanding()) {
			if(x.contentEquals(current_string)) {
				m_Selected_Antibanding = pos;
				break;
			}			
			pos++;
		}
		
		// color effects
		current_string = m_camera_param.getColorEffect();
		pos = 0;		
		for ( String x : m_camera_param.getSupportedColorEffects()) {
			if(x.contentEquals(current_string)) {
				m_Selected_ColorEffects = pos;
				break;
			}			
			pos++;
		}
		
		// flash mode
		current_string = m_camera_param.getFlashMode();
		pos = 0;		
		for ( String x : m_camera_param.getSupportedFlashModes()) {
			if(x.contentEquals(current_string)) {
				m_Selected_FlashModes = pos;
				break;
			}			
			pos++;
		}
		
		// focus mode
		current_string = m_camera_param.getFocusMode();
		pos = 0;		
		for ( String x : m_camera_param.getSupportedFocusModes()) {
			if(x.contentEquals(current_string)) {
				m_Selected_FocusModes = pos;
				break;
			}			
			pos++;
		}
		
		// Scene mode
		current_string = m_camera_param.getSceneMode();
		pos = 0;		
		for ( String x : m_camera_param.getSupportedSceneModes()) {
			if(x.contentEquals(current_string)) {
				m_Selected_SceneMode = pos;
				break;
			}			
			pos++;
		}
		
		// White balance
		current_string = m_camera_param.getWhiteBalance();
		pos = 0;		
		for ( String x : m_camera_param.getSupportedWhiteBalance()) {
			if(x.contentEquals(current_string)) {
				m_Selected_WhiteBalance = pos;
				break;
			}			
			pos++;
		}
	}
	
	public void updateParameters() {
		String			current_string;
		int				current_int;
		Camera.Size		current_size;
		
		try {
			// Anti-banding
			if(m_Selected_Antibanding != -1) {
				current_string = m_camera_param.getSupportedAntibanding().get(m_Selected_Antibanding);
				m_camera_param.setAntibanding(current_string);
			}
			
			// colour effects
			if(m_Selected_ColorEffects != -1) {
				current_string = m_camera_param.getSupportedColorEffects().get(m_Selected_ColorEffects);
				m_camera_param.setColorEffect(current_string);
			}
			
			// flash modes
			if(m_Selected_FlashModes != -1) {
				current_string = m_camera_param.getSupportedFlashModes().get(m_Selected_FlashModes);
				m_camera_param.setFlashMode(current_string);
			}
			
			// focus mode
			if(m_Selected_FocusModes != -1) {
				current_string = m_camera_param.getSupportedFocusModes().get(m_Selected_FocusModes);
				m_camera_param.setFocusMode(current_string);
			}
			
			// picture format
			if(m_Selected_PictureFormat != -1) {
				current_int = m_camera_param.getSupportedPictureFormats().get(m_Selected_PictureFormat);
				m_camera_param.setPictureFormat(current_int);
			}
			
			// picture size
			if(m_Selected_PictureSize != -1) {
				current_size = m_camera_param.getSupportedPictureSizes().get(m_Selected_PictureSize);
				m_camera_param.setPictureSize(current_size.width, current_size.height);
			}
			
			// preview format
			if(m_Selected_PreviewFormat != -1) {
				current_int = m_camera_param.getSupportedPreviewFormats().get(m_Selected_PreviewFormat);
				m_camera_param.setPreviewFormat(current_int);
			}
			
			// preview size
			if(m_Selected_PreviewSize != -1) {
				current_size = m_camera_param.getSupportedPreviewSizes().get(m_Selected_PreviewSize);
				m_camera_param.setPreviewSize(current_size.width, current_size.height);
			}
			
			// preview frame rate
			if(m_Selected_PreviewFrameRate != -1) {
				current_int = m_camera_param.getSupportedPreviewFrameRates().get(m_Selected_PreviewFrameRate);
				m_camera_param.setPreviewFrameRate(current_int);
			}
			
			// scene mode
			if(m_Selected_SceneMode != -1) {
				current_string = m_camera_param.getSupportedSceneModes().get(m_Selected_SceneMode);
				m_camera_param.setSceneMode(current_string);
			}
			
			// white balance
			if(m_Selected_WhiteBalance != -1) {
				current_string = m_camera_param.getSupportedWhiteBalance().get(m_Selected_WhiteBalance);
				m_camera_param.setWhiteBalance(current_string);
			}
			
			m_camera.setParameters(m_camera_param);
		} catch (Exception e) {
			Log.e(TAG, "updateParameters:" + e.toString());
		}
	}

	public void setHolder(SurfaceHolder holder) {
		try {			
			m_camera.setPreviewDisplay(holder);
			m_previewholder_set = true;
		} catch (Exception e) {
			Log.e(TAG, "startPreview:" + e.toString());
		}
	}
	
	public void startPreview() {
		try {
			if(m_previewholder_set == true) {
				Log.d(TAG, "Register preview callback...");
				m_camera.setPreviewCallback(m_preview_cb);				
				m_camera.startPreview();
				m_preview_started = true;
			}
		} catch (Exception e) {
			Log.e(TAG, "startPreview:" + e.toString());
		}
	}
	
	public void stopPreview() {
		if(m_preview_started == true) {
			m_camera.stopPreview();
			m_preview_started = false;
		}
	}
	
	private Camera.AutoFocusCallback m_auto_focus_cb = new AutoFocusCallback() {

		@Override
		public void onAutoFocus(boolean success, Camera camera) {
			Log.d(TAG, "AutoFocus >>>>>");
			
			Log.d(TAG, "AutoFocus <<<<<");
		}		
	};
	
	public void TakePicture() {
		m_camera.takePicture(m_shutter_cb, m_picture_raw_cb, m_picture_jpeg_cb);
	}
	
	private Camera.PictureCallback m_picture_raw_cb = new PictureCallback() {

		@Override
		public void onPictureTaken(byte[] data, Camera camera) {
			if(data != null) {
				Log.d(TAG, "Start of Picture RAW >>>>>");
				
				m_current_taken_raw_frame_no++;
				
				Log.d(TAG, "End of Picture RAW#" + m_current_taken_raw_frame_no + " <<<<<");
			}
		}		
	};
	
	private Camera.PictureCallback m_picture_jpeg_cb = new PictureCallback() {

		@Override
		public void onPictureTaken(byte[] data, Camera camera) {
			if(data != null) {
				Log.d(TAG, "Start of Picture JPEG >>>>>");
				
				m_current_taken_jpeg_frame_no++;
				
				if(m_preview_started == true) {
					// restart preview
					m_camera.startPreview();
				}
				
				Log.d(TAG, "End of Picture JPEG#" + m_current_taken_jpeg_frame_no + " <<<<<");
			}
		}		
	};
	
	private Camera.PreviewCallback m_preview_cb = new Camera.PreviewCallback() {

		@Override
		public void onPreviewFrame(byte[] data, Camera camera) {
			Log.d(TAG, "Preview callback >>>>>");
			
			if(data != null) {
				m_current_preview_frame_no++;
				
				Log.d(TAG, "Preview #" + m_current_preview_frame_no);
			} else {
				Log.w(TAG, "data = null!!!");
			}
			
			Log.d(TAG, "Preview callback <<<<<");
		}		
	};
	
	private Camera.ShutterCallback m_shutter_cb = new Camera.ShutterCallback() {

		@Override
		public void onShutter() {
			Log.d(TAG, "Shutter >>>>>");
			
			Log.d(TAG, "Shutter <<<<<");
		}		
	};
	
	private Camera.ErrorCallback m_error_cb = new Camera.ErrorCallback() {

		@Override
		public void onError(int error, Camera camera) {
			Log.d(TAG, "Error >>>>>");
			
			Log.e(TAG, "code:" + error);
			
			Log.d(TAG, "Error <<<<<");			
		}		
	};
	
	public boolean IsCameraClosed() {
		return m_close_is_called;
	}

	public boolean IsPreviewRun() {
		return m_preview_started;
	}	
}
